#include "bdf_to_cdb_convert.h"

#include <fstream>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <regex>

int BdfToCdbConvert::convert(const std::string &bdfPath, const std::string &cdbPath)
{
    if (parsedBdfFile(bdfPath) != 0)
    {
        std::cerr << "Failed to parse BDF file ." << std::endl;
        return -1;
    }

    return generateCdbFile(cdbPath);
}

int BdfToCdbConvert::parsedBdfFile(const std::string &bdfPath)
{
    std::ifstream infile(bdfPath);
    if (!infile.is_open())
    {
        std::cerr << "Unable to open BDF file: " << bdfPath << std::endl;
        return -1;
    }

    std::string line;
    while (std::getline(infile, line))
    {
        trim(line);
        if (line.empty())
            continue;
        if (line.find("GRID") == 0)
        {
            parseNode(line);
        }
        else if (line.find("C") == 0)
        {
            parseElement(line);
        }
         else if (line.find("PSHELL") == 0)
        {
            parsePshell(line);
        }
        else if (line.find("MAT1") == 0)
        {
            parseMat1(line);
        }
    }
    return 0;
}

void BdfToCdbConvert::parseNode(const std::string &line)
{
    if (line.substr(0, 4) != "GRID")
        return;

    std::regex pattern(R"(GRID\s+(\d+)\s+([-\d.Ee+]+)([-\d.Ee+]+)([-\d.Ee+]+))");
    std::smatch match;


     try {
        Node node;
        node.id = std::stoi(line.substr(8, 8));
        node.x = std::stod(line.substr(24, 8));
        node.y = std::stod(line.substr(32, 8));
        node.z = std::stod(line.substr(40, 8));
        nodes.push_back(node);
    }
    catch (const std::exception& e) {
        std::cerr << "Error parsing node line: " << line << "\nReason: " << e.what() << std::endl;
    }
}



std::string BdfToCdbConvert::mapBdfElementTypeToCdb(const std::string &bdfType)
{
    if (bdfType == "CQUAD4")
        return "SHELL181";
    if (bdfType == "CTRIA3")
        return "SHELL181";
    if (bdfType == "CHEXA")
        return "SOLID185";
    if (bdfType == "CTETRA")
        return "SOLID185";
    return "UNKNOWN";
}


void BdfToCdbConvert::parseElement(const std::string& line) {
    if (line.substr(0, 6) != "CQUAD4") return;

    if(line.size() < 56) {
        std::cerr << "Invalid CQUAD4 format: " << line << std::endl;
        return;
    } 

    try {
        Element elem;
        elem.type = "CQUAD4";
        elem.id   = std::stoi(line.substr(8, 8));    // 元素 ID
        elem.pid  = std::stoi(line.substr(16, 8));   // 属性 ID (PID)

        // 提取四个节点 ID，每个宽度 8
        elem.nodes.push_back(std::stoi(line.substr(24, 8)));
        elem.nodes.push_back(std::stoi(line.substr(32, 8)));
        elem.nodes.push_back(std::stoi(line.substr(40, 8)));
        elem.nodes.push_back(std::stoi(line.substr(48, 8)));

        elements.push_back(elem);
    } catch (const std::exception& e) {
        std::cerr << "Error parsing CQUAD4 line: " << line << "\n"
                  << "Reason: " << e.what() << std::endl;
    }
}

int BdfToCdbConvert::generateCdbFile(const std::string &path)
{
    std::ofstream out(path);
    if (!out.is_open()) {
        std::cerr << "Failed to write CDB file: " << path << std::endl;
        return -1;
    }

    // 构建 thickness -> sectypeId 映射表
    std::unordered_map<double, int> thicknessToSectypeId;
    std::vector<double> thicknessList = {1.6, 2.0, 3.0, 4.0, 6.0};
    int baseSectypeId = 10005817;

    for (size_t i = 0; i < thicknessList.size(); ++i) {
        thicknessToSectypeId[thicknessList[i]] = baseSectypeId + static_cast<int>(i);
    }
    
    writeHeader(out);
    out << "/PREP7\n";
    out << "/NOPR\n";
    out << "C***       Element Types\n";
    out << "ETBLOCK,         1,         1\n";
    out << "(21i9)\n";
    out << std::setw(9) << 1 << std::setw(9) << 181;
    for (int i = 0; i < 19; ++i)
        out << std::setw(9) << 0;
    out << "\n" << std::setw(9) << -1 << "\n";

    out << "C***       Nodes\n";
    out << "CSYS,0\n";
    char headerLine[64] = {};
    snprintf(headerLine, sizeof(headerLine), "NBLOCK,6,SOLID,%10d,%10lu\n", 12332917, nodes.size());
    out << headerLine;
    out << "(3i9,6e21.13e3)\n";
    for (const auto &node : nodes) {
        out << std::setw(9) << node.id
            << std::setw(9) << 0
            << std::setw(9) << 0;

       auto writeCoord = [&](double val) {
    std::ostringstream oss;
    oss << std::uppercase << std::scientific << std::setprecision(13);
    oss << val;
    std::string s = oss.str();

    // 补齐指数为两位数，例如 E+3 -> E+03
    size_t ePos = s.find('E');
    if (ePos != std::string::npos) {
        char sign = s[ePos + 1];
        std::string exp = s.substr(ePos + 2);
        if (exp.size() == 1) exp = "0" + exp;
        else if (exp.size() == 0) exp = "00";
        s = s.substr(0, ePos + 2) + exp;
    }

    out << std::setw(21) << s;
};

        writeCoord(node.x);
        writeCoord(node.y);
        writeCoord(node.z);
        out << "\n";
    }

    out << "N,R5.3,LOC,       -1,\n";
    out << "C***       Elements\n";
    out << "EBLOCK,19,SOLID,\n";
    out << "(19i9)\n";

    for (const auto &elem : elements) {
        int pid = elem.pid;
        int mid = pidToProperty.count(pid) ? pidToProperty[pid].mid : -1;
        double thickness = pidToProperty.count(pid) ? pidToProperty[pid].thickness : 1.6;

        int sectypeId = baseSectypeId;
        if (thicknessToSectypeId.count(thickness)) {
            sectypeId = thicknessToSectypeId[thickness];
        }

        out << std::setw(9) << 705002
            << std::setw(9) << 1
            << std::setw(9) << sectypeId
            << std::setw(9) << sectypeId
            << std::setw(9) << 0 << std::setw(9) << 0
            << std::setw(9) << 0 << std::setw(9) << 0
            << std::setw(9) << 4 << std::setw(9) << 0
            << std::setw(9) << elem.id; 

        for (int nid : elem.nodes) {
            out << std::setw(9) << nid;
        }
        out << "\n";
    }

    out << std::setw(9) << -1 << "\n";
    out << "C***       Element properties\n";

    for (double thickness : thicknessList) {
        int sectypeId = thicknessToSectypeId[thickness];

        out << "SECTYPE," << sectypeId << ",SHELL\n";

        std::ostringstream oss;
        oss << std::fixed << std::setprecision(1) << thickness;
        std::string thicknessStr = oss.str();
        if (thicknessStr.back() == '0') {
            thicknessStr.pop_back();
        }
        out << "SECDATA," << thicknessStr << ",705002,\n";
    }

    out << "FINISH\n";
    out.close();

    std::cout << "[CDB] Wrote file: " << path
              << " | Nodes: " << nodes.size()
              << " | Elements: " << elements.size() << "\n";

    return 0;
}

void BdfToCdbConvert::trim(std::string &s)
{
    size_t start = s.find_first_not_of(" \t\r\n");
    size_t end = s.find_last_not_of(" \t\r\n");
    s = (start == std::string::npos) ? "" : s.substr(start, end - start + 1);
}

std::vector<std::string> BdfToCdbConvert::split(const std::string &s, char delimiter)
{
    std::vector<std::string> tokens;
    std::stringstream ss(s);
    std::string item;
    while (std::getline(ss, item, delimiter))
    {
        trim(item);
        tokens.push_back(item);
    }
    return tokens;
}

void BdfToCdbConvert::parsePshell(const std::string& line) {
    if (line.substr(0, 6) != "PSHELL") return;

    std::istringstream iss(line);
    std::string key;
    int pid, mid;
    double t;
    iss >> key >> pid >> mid >> t;

    pidToProperty[pid] = {mid, t};

    if(pidToSectionId.find(pid) != pidToSectionId.end()) {
        pidToSectionId[pid] = sectionIdBase++;
    }
}


void BdfToCdbConvert::parseMat1(const std::string& line) {
   if(line.substr(0, 4) != "MAT1") return;
   std::istringstream iss(line);
   std::string key;
   int mid;
   double e, nu;
   iss >> key >> mid >> e >> nu;
   midToMaterial[mid] = {mid, e, nu};
}

void BdfToCdbConvert::writeHeader(std::ofstream& out) {
    std::time_t now = std::time(nullptr);
    std::tm* localTime = std::localtime(&now);
    char timeStr[100];
    std::strftime(timeStr, sizeof(timeStr), "/COM, VKI 132352 %d-%b-%y %H:%M:%S\n", localTime);
    out << timeStr;
}